---
title: Token Transitions
description: Collapse annotation example
layout: PreviewAndImplementation
---

## !demo

Animate the transition between code blocks at a token level.

<Demo name="token-transitions" />

## !implementation

We are going to use some utils from `codehike/utils/token-transitions`. The easiest way of using them is inside a class component:

```tsx smooth-pre.tsx -cw
"use client"

import { CustomPreProps, InnerPre, getPreRef } from "codehike/code"
import {
  TokenTransitionsSnapshot,
  calculateTransitions,
  getStartingSnapshot,
} from "codehike/utils/token-transitions"
import React from "react"

const MAX_TRANSITION_DURATION = 900 // milliseconds

export class SmoothPre extends React.Component<CustomPreProps> {
  ref: React.RefObject<HTMLPreElement>
  constructor(props: CustomPreProps) {
    super(props)
    this.ref = getPreRef(this.props)
  }

  render() {
    return <InnerPre merge={this.props} style={{ position: "relative" }} />
  }

  getSnapshotBeforeUpdate() {
    return getStartingSnapshot(this.ref.current!)
  }

  componentDidUpdate(
    prevProps: never,
    prevState: never,
    snapshot: TokenTransitionsSnapshot,
  ) {
    const transitions = calculateTransitions(this.ref.current!, snapshot)
    transitions.forEach(({ element, keyframes, options }) => {
      const { translateX, translateY, ...kf } = keyframes as any
      if (translateX && translateY) {
        kf.translate = [
          `${translateX[0]}px ${translateY[0]}px`,
          `${translateX[1]}px ${translateY[1]}px`,
        ]
      }
      element.animate(kf, {
        duration: options.duration * MAX_TRANSITION_DURATION,
        delay: options.delay * MAX_TRANSITION_DURATION,
        easing: options.easing,
        fill: "both",
      })
    })
  }
}
```

Then we create the annotation handler:

```tsx code.tsx -cw
import { AnnotationHandler, InnerToken } from "codehike/code"
import { SmoothPre } from "./smooth-pre"

export const tokenTransitions: AnnotationHandler = {
  name: "token-transitions",
  PreWithRef: SmoothPre,
  Token: (props) => (
    <InnerToken merge={props} style={{ display: "inline-block" }} />
  ),
}
```

And then pass the handler to the `Pre` component:

```tsx page.tsx -c
async function Code({ codeblock }: { codeblock: RawCode }) {
  const highlighted = await highlight(codeblock, "github-dark")
  return <Pre code={highlighted} handlers={[tokenTransitions]} />
}
```

When the `codeblock` changes, the `Code` component will animate the transition between the old and new code blocks.

## See it in action

- [Scrollycoding layout example](/docs/layouts/scrollycoding)
- [Spotlight layout example](/docs/layouts/spotlight)
